/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.jfonia.view.panels;

import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.jfonia.connect5.basics.BasicValueNode;
import org.jfonia.connect5.basics.MutableValueNode;
import org.jfonia.connect5.intervals.BasicDoubleInterval;
import org.jfonia.connect5.intervals.LinkedDoubleIntervalList;
import org.jfonia.connect5.numerical.UnidirectionalDoubleSumRelation;
import org.jfonia.connect5.relations.Equal;
import org.jfonia.constants.ViewConstants;
import org.jfonia.model.IListObserver;
import org.jfonia.view.labels.Label;
import org.jfonia.view.main.LeadSheet;
import org.jfonia.model.StaffCollection;
import org.jfonia.view.StaffCollectionView;
import org.jfonia.view.labels.ILabelsObserver;
import org.jfonia.notation.Notation;
import org.jfonia.view.StaffView;
import org.jfonia.view.relations.DoubleInversionRelation;

/**
 *
 * @author Rik Bauwens
 */
public class ViewPanel extends BasicNodePanel implements IListObserver<StaffCollection>, 
                                                         MouseListener, ILabelsObserver,
                                                         Iterable<StaffCollectionView>
{
        private LeadSheet leadSheet;

        private ViewLayerPanel viewLayerPanel;

        private LinkedDoubleIntervalList xNodes;
        private LinkedDoubleIntervalList yNodes;
        private List<StaffCollectionView> staffCollectionViews;

        private MutableValueNode<Boolean> viewSelectionNode;

        public ViewPanel(ViewLayerPanel viewLayerPanel,
                         MutableValueNode<Double> xNode, MutableValueNode<Double> yNode,
                         MutableValueNode<Double> widthNode, MutableValueNode<Double> heightNode)
        {
                super(xNode, yNode, widthNode, heightNode);
                this.viewLayerPanel = viewLayerPanel;
                setBackground(ViewConstants.MAIN_BACKGROUND_COLOR);
                addMouseListener(this);

                xNodes = new LinkedDoubleIntervalList();
                new Equal<Double>(xNode, xNodes.getBeginNode());
                yNodes = new LinkedDoubleIntervalList();
                new Equal<Double>(yNode, yNodes.getBeginNode());

                MutableValueNode<Double> invertedXNode = new BasicValueNode<Double>(0.0);
                new DoubleInversionRelation(xNodes.getBeginNode(), invertedXNode);
                new UnidirectionalDoubleSumRelation(xNodes.getEndNode(), invertedXNode, this.widthNode);
                new Equal<Double>(this.widthNode, widthNode);

                MutableValueNode<Double> invertedYNode = new BasicValueNode<Double>(0.0);
                new DoubleInversionRelation(yNodes.getBeginNode(), invertedYNode);
                new UnidirectionalDoubleSumRelation(yNodes.getEndNode(), invertedYNode, this.heightNode);
                new Equal<Double>(this.heightNode, heightNode);

                staffCollectionViews = new LinkedList<StaffCollectionView>();
                viewSelectionNode = new BasicValueNode<Boolean>(false);
        }
        
        private boolean hasStaffCollectionViewFocus()
        {
                for(StaffCollectionView staffCollectionView : staffCollectionViews)
                {
                        if(staffCollectionView.hasFocus())
                                return true;
                }
                return false;
        }

        @Override
        public boolean hasFocus()
        {
                return /*super.hasFocus() || */hasStaffCollectionViewFocus();
        }
        
        public void setLeadSheet(LeadSheet leadSheet)
        {
                if(this.leadSheet != null)
                {
                        this.leadSheet.removeObserver(this);
                        leadSheet.getLabels().removeObserver(this);
                }
                this.leadSheet = leadSheet;
                if(leadSheet == null)
                        return;
                leadSheet.addObserver(this);
                for(StaffCollection staffCollection : leadSheet)
                        addStaffCollection(staffCollection);
                leadSheet.getLabels().addObserver(this);
                for(Label label : leadSheet.getLabels())
                        staffCollectionViews.get(label.getStaffCollectionId())
                                                 .setBoxPanelSelected(label, true);
        }

        public double getX(int tick)
        {
                return staffCollectionViews.iterator().hasNext() ? staffCollectionViews.iterator().next().getX(tick) : 0;
        }

        public MutableValueNode<Boolean> getViewSelectionNode()
        {
                return viewSelectionNode;
        }

        public int getStaffCollectionViewlIndex(StaffCollection staffCollection)
        {
                int i = 0;
                while(i < staffCollectionViews.size() &&
                      staffCollectionViews.get(i).getStaffCollection() != staffCollection)
                        i++;

                if(i < staffCollectionViews.size())
                        return i;
                return -1;
        }

        public void addStaffCollection(StaffCollection staffCollection)
        {
                //Only add staffcollection when no view with the same
                //staffcollection exists already
                if(getStaffCollectionViewlIndex(staffCollection) < 0)
                {
                        BasicDoubleInterval xInterval = new BasicDoubleInterval();
                        BasicDoubleInterval yInterval = new BasicDoubleInterval();
                        xNodes.add(xInterval);
                        int index = xNodes.indexOf(xInterval);
                        yNodes.insert(index, yInterval);
                        //Prevent out of bounds exception
                        while(index >= yNodes.size())
                                yNodes.add(new BasicDoubleInterval());
                        staffCollectionViews.add(index, new StaffCollectionView(this, viewLayerPanel, staffCollection,
                                                 xInterval.getBeginNode(), yInterval.getBeginNode(),
                                                 xInterval.getDifferenceNode(), yInterval.getDifferenceNode()));
                }
        }

        public void removeStaffCollection(StaffCollection staffCollection)
        {
                //Staffcollection can only be removed when there is a view
                //containing it
                int index = getStaffCollectionViewlIndex(staffCollection);
                if(index >= 0)
                {
                        xNodes.remove(index);
                        yNodes.remove(index);
                        staffCollectionViews.remove(index);
                }
        }

        public LeadSheet getLeadSheet()
        {
                return leadSheet;
        }

        //Add staffcolection
        public void itemAdded(StaffCollection item)
        {
                addStaffCollection(item);
        }

        //Remove staffcollection
        public void itemRemoved(StaffCollection item)
        {
                removeStaffCollection(item);
        }

        public void labelAdded(Label label)
        {
                staffCollectionViews.get(label.getStaffCollectionId())
                        .setBoxPanelSelected(label, true);
        }

        public void labelRemoved(Label label)
        {
                staffCollectionViews.get(label.getStaffCollectionId())
                        .setBoxPanelSelected(label, false);
        }

        @Override
        public void paintComponent(Graphics g)
        {
                super.paintComponent(g);
                for(StaffCollectionView staffCollectionView : staffCollectionViews)
                        staffCollectionView.paintComponent(g);
                /*Graphics2D g2 = (Graphics2D) g;
                GradientPaint gradientColorSouth = new GradientPaint(0, 0,
                        ViewConstants.SCRIBBLE_PANEL_BACKGROUND_COLOR,
                        20, 0, Color.WHITE);
                g2.setPaint(gradientColorSouth);
                g2.fillRect(0, getHeight() - 20, getWidth(), getHeight());*/
        }

        public void mouseClicked(MouseEvent e)
        {
                Notation.getInstance().clearSelectedElements();
                requestFocus();
                for(StaffCollectionView staffCollectionView : staffCollectionViews)
                {
                        for(StaffView staffView : staffCollectionView)
                        {
                                if(e.getY() >= staffView.getYOrientation().getBegin()
                                   && e.getY() <= staffView.getYOrientation().getEnd())
                                    ;

                        }
                }
        }

        public void mousePressed(MouseEvent e) {}

        public void mouseReleased(MouseEvent e) {}

        public void mouseEntered(MouseEvent e) {}

        public void mouseExited(MouseEvent e) {}

        public Iterator<StaffCollectionView> iterator()
        {
                return staffCollectionViews.iterator();
        }
}
